package com.yuntsg.subject_database.servic;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.json.JSONUtil;
import com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass;
import com.yuntsg.subject_database.entity.PublicEntity;
import com.yuntsg.subject_database.util.OftenUtils;
import com.yuntsg.subject_database.util.PublicUtils;
import lombok.Data;

import java.io.IOException;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 本程序的功能是统计每个单位的发文量、被引量、第一作者、通讯作者、六大刊、中科院（1、2、3、4、0）、北大核心、CSCD、统计源、中华刊、PubMed、cite的发文量；
 * 中科院大类、小类名称；北大核心大类、小类名称；学院；部门；作者（优先中文作者）；英文关键词；中文关键词；合作单位等的词云，得分大致按影响因子*被引次数，不按出现的篇数。
 * 本次对国家教育部的规范的中文名字、国家、省份、城市，也作了相似度计算，仅作为参考。此仅计算大于 1w 篇的单位名称。后续给修改为更新，因为耗时太多了。
 * 贾新志 2021.10.23
 */
// 此部分为计算单位及作者个人的发文量、被引量等数据。
public class SubjectDatabaseUnit {

    // 如果idAndAuth包含"Łł"，说明它没有对应的作者唯一id号，如果authIdAndAuthMap.containsKey(idAndAuth)说明此idAndAuth是作者的唯一id号（此id号在保存时已转成了String，所以可以直接get），若是唯一id号，则可以获取其相对应的作者List，因此，共有两种不同的数据，然后插表。
    public static void insertAuthDateTable(Map<String, Map<String, Map<Integer, Integer>>> authTypeYearNumMap, Map<String, Map<String, Map<String, Double>>> authTypeNameDoubleMap, String nameTempSingleTable,
                                           Map<String, Set<Integer>> authAidListMap, Map<String, Double> authAllScoreMap,
                                           Map<String, PublicEntity.TypeAndRank> unitOrAuthAndRankMap, List<PublicEntity.TableField> tableList, boolean isInsertTable) {
        authTypeYearNumMap.keySet().parallelStream().forEach(enAuth -> {
            if (unitOrAuthAndRankMap.containsKey(enAuth)) {
                PublicEntity.TypeAndRank typeAndRank = unitOrAuthAndRankMap.get(enAuth);
                Set<Integer> aidList = new HashSet<>();
                if (authAidListMap.containsKey(enAuth)) {
                    aidList = authAidListMap.get(enAuth);
                }
                double score = 0.0;
                if (authAllScoreMap.containsKey(enAuth)) {
                    score = authAllScoreMap.get(enAuth);
                }
                Map<String, Map<Integer, Integer>> typeYearNumMap = authTypeYearNumMap.get(enAuth);
                Map<String, Map<String, Double>> typeNameDoubleMap = new ConcurrentHashMap<>();
                if (authTypeNameDoubleMap.containsKey(enAuth)) {
                    typeNameDoubleMap = authTypeNameDoubleMap.get(enAuth);
                }
                Map<String, Map<String, Double>> saveTypeNameDoubleMap = changeDouMap(typeNameDoubleMap);
                insertAuthTable(nameTempSingleTable, enAuth, typeYearNumMap, saveTypeNameDoubleMap, aidList, score, typeAndRank, tableList, isInsertTable);
            }
        });
    }

    // 插入作者表
    public static void insertAuthTable(String nameTempSingleTable, String info, Map<String, Map<Integer, Integer>> typeYearNumMap, Map<String, Map<String, Double>> saveTypeNameDoubleMap,
                                       Set<Integer> aidList, Double score, PublicEntity.TypeAndRank typeAndRank,
                                       List<PublicEntity.TableField> tableList, boolean isInsertTable) {
        String key = typeYearNumMap.size() > 0 ? JSONUtil.toJsonStr(typeYearNumMap) : "";
        String value = saveTypeNameDoubleMap.size() > 0 ? JSONUtil.toJsonStr(saveTypeNameDoubleMap) : "";
        String aid_list = aidList.size() > 0 ? JSONUtil.toJsonStr(aidList) : "";
        PublicUtils.insertTable(nameTempSingleTable, key, value, typeAndRank.getCode(), "作者信息" + typeAndRank.getType(), typeAndRank.getRank(), score, aidList.size(), typeAndRank.getSpecialist(), typeAndRank.getJid(), info, aid_list, tableList, isInsertTable);
    }


    // 读表，包括论文（article）、国自然基金（nsfc）、处理单位数据（univ）、处理个人数据（auth）、获取作者与作者相对应的作者ID号（authId）、更新同名同姓表（scholar.result_auth_same_name）六个部分。其中，处理单位的和处理个人的，读的同一张表（scholar.data_auth_same_data）
    public static void readAuthTable(String saveTable, Map<Integer, SubjectDatabaseWord.ArticleInfo> allArticleInfoMap, Map<String, Map<String, List<NsfcInfo>>> nsfcInfoMap, Map<String, Map<String, PublicEntity.TopLower>> ruleMap,
                                     Map<String, PublicEntity.TypeAndRank> unitOrAuthAndRankMap, List<PublicEntity.TableField> tableList, boolean isInsertTable) throws IOException {
        Map<String, Map<String, Map<Integer, Integer>>> authTypeYearNumMap = new ConcurrentHashMap<>();
        Map<String, Map<String, Map<String, Double>>> authTypeNameDoubleMap = new ConcurrentHashMap<>();
        Set<String> nsfcOnceSet = new ConcurrentHashSet<>();
        Map<String, Set<Integer>> authAidListMap = new ConcurrentHashMap<>();
        Map<String, Double> authAllScoreMap = new ConcurrentHashMap<>();
        for (Map.Entry<Integer, SubjectDatabaseWord.ArticleInfo> entry : allArticleInfoMap.entrySet()) {
            List<SubjectDatabaseWord.DataInfo> dataInfoList = entry.getValue().getDataInfoList();
            if (dataInfoList != null) {
                for (SubjectDatabaseWord.DataInfo dataInfo : dataInfoList) {
                    try {
                        getAuth(entry.getKey(), dataInfo, ruleMap, allArticleInfoMap, authTypeYearNumMap, authTypeNameDoubleMap, nsfcInfoMap, nsfcOnceSet, authAidListMap, authAllScoreMap, unitOrAuthAndRankMap);
                    } catch (Exception e) {

                    }
                }
            }

        }
        insertAuthDateTable(authTypeYearNumMap, authTypeNameDoubleMap, saveTable, authAidListMap, authAllScoreMap, unitOrAuthAndRankMap, tableList, isInsertTable);
    }

    // 读表，包括论文（article）、国自然基金（nsfc）、处理单位数据（univ）、处理个人数据（auth）、获取作者与作者相对应的作者ID号（authId）、更新同名同姓表（scholar.result_auth_same_name）六个部分。其中，处理单位的和处理个人的，读的同一张表（scholar.data_auth_same_data）
    public static void readUnivTable(String saveTable, Map<Integer, SubjectDatabaseWord.ArticleInfo> allArticleInfoMap, Map<String, Map<String, List<NsfcInfo>>> nsfcInfoMap, Map<String, Map<String, PublicEntity.TopLower>> ruleMap,
                                     Map<String, PublicEntity.TypeAndRank> unitOrAuthAndRankMap, List<PublicEntity.TableField> tableList, boolean isInsertTable) throws SQLException, IOException {
        Map<String, Map<String, Map<Integer, Integer>>> univTypeYearNumMap = new ConcurrentHashMap<>();
        Map<String, Map<String, Map<String, Double>>> univTypeNameDoubleMap = new ConcurrentHashMap<>();
        Map<String, Set<Integer>> univAidListMap = new ConcurrentHashMap<>();
        Map<String, Double> univAllScoreMap = new ConcurrentHashMap<>();
        Set<String> nsfcOnceSet = new ConcurrentHashSet<>();
        for (Map.Entry<Integer, SubjectDatabaseWord.ArticleInfo> entry : allArticleInfoMap.entrySet()) {
            List<SubjectDatabaseWord.DataInfo> dataInfoList = entry.getValue().getDataInfoList();
            if (dataInfoList != null) {
                for (SubjectDatabaseWord.DataInfo dataInfo : dataInfoList) {
                    try {
                        getUnit(entry.getKey(), dataInfo, ruleMap, allArticleInfoMap, univTypeYearNumMap, univTypeNameDoubleMap, nsfcInfoMap, nsfcOnceSet, univAidListMap, univAllScoreMap, unitOrAuthAndRankMap);
                    } catch (Exception e) {

                    }
                }
            }

        }
        insertUnitStatisticsTable(saveTable, univTypeYearNumMap, univTypeNameDoubleMap, univAidListMap, univAllScoreMap, unitOrAuthAndRankMap, tableList, isInsertTable);
    }

    // 读表，包括论文（article）、国自然基金（nsfc）、处理单位数据（univ）、处理个人数据（auth）、获取作者与作者相对应的作者ID号（authId）、更新同名同姓表（scholar.result_auth_same_name）六个部分。其中，处理单位的和处理个人的，读的同一张表（scholar.data_auth_same_data）
    public static Map<String, Map<String, List<NsfcInfo>>> readNsfcTable(String table, Integer onePageNum, String field, String limit, String autoId, Map<String, Map<String, PublicEntity.TopLower>> ruleMap, boolean print) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Integer> tempNum = Collections.synchronizedList(new ArrayList<>());
        Map<String, Map<String, List<NsfcInfo>>> nsfcInfoMap = new ConcurrentHashMap<>();
        int from = 1;
        int max = 0;
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + table);
        int count = tableNum.get(0).getInt("count");
        if (count > 0) {
            max = Db.use().queryNumber("select max(" + autoId + ") from " + table).intValue();
        }
        int page = max / onePageNum;
        if (max % onePageNum != 0) {
            page++;
        }
        List<OftenUtils.PageInfo> pageInfos = new ArrayList<>();
        for (int i = 1; i <= page; i++) {
            int start = (i - 1) * onePageNum + from;
            int stop = i * onePageNum + from - 1;
            if (stop > max) {
                stop = max + onePageNum;
            }
            pageInfos.add(new OftenUtils.PageInfo(start, stop));
        }
        int numAll = pageInfos.size();

        pageInfos.parallelStream().forEach(pg -> {
            try {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + table + " where " + autoId + " between " + pg.getFrom() + " and " + pg.getTo() + limit);
                tempNum.add(pg.getFrom());
                if (print && tempNum.size() % 1000 == 0) {
                    System.out.println(format.format(new Date()) + " 现在开始： " + pg.getFrom() + " -- " + pg.getTo() + " 总条数： " + numAll + " 目前已处理： " + tempNum.size() + " 表名： " + table);
                    OftenUtils.printMemory();
                }
                for (Entity entry : tableData) {
                    getNsfc(entry, nsfcInfoMap, ruleMap);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
        return nsfcInfoMap;
    }

    // unitAndDept->单位与部门的集合，unitSet->单位的集合，unitAndAuth->单位与作者的集合，这里的单位包含大学和医院。nsfcOnceSet是国自然对单位及对作者，都仅仅计算一次，保存进去了，下次就不计算了。
    public static void getUnit(Integer aid, SubjectDatabaseWord.DataInfo dataInfo, Map<String, Map<String, PublicEntity.TopLower>> ruleMap, Map<Integer, SubjectDatabaseWord.ArticleInfo> allArticleInfoMap, Map<String, Map<String, Map<Integer, Integer>>> univTypeYearNumMap, Map<String, Map<String, Map<String, Double>>> univTypeNameDoubleMap,
                               Map<String, Map<String, List<NsfcInfo>>> nsfcInfoMap, Set<String> nsfcOnceSet, Map<String, Set<Integer>> univAidListMap, Map<String, Double> univAllScoreMap, Map<String, PublicEntity.TypeAndRank> unitOrAuthAndRankMap) throws IOException {
        Map<String, Set<String>> unitAndDept = new ConcurrentHashMap<>();
        Set<String> unitSet = new ConcurrentHashSet<>();
        Map<String, Set<String>> unitAndAuth = new ConcurrentHashMap<>();
        if (aid > 0) {
            // 同一篇文章，可能同一单位的，有多个作者，比如：作者a、b、c，那么这些作者可能有些是通讯，有些是第一，所以，必须将本单位下的所有作者都弄出来，再判断是否是通讯或第一才是正确的。
            String auth = PublicUtils.reName(dataInfo.getAuth());
            String cnAu = PublicUtils.reName(dataInfo.getCnAu());
            Set<String> hospSet = addUnitSet(dataInfo.getHosp(), unitSet, ruleMap, "hosp");
            Set<String> univSet = addUnitSet(dataInfo.getUniv(), unitSet, ruleMap, "univ");
            getUnitAndDept(ruleMap, hospSet, dataInfo.getDept(), unitAndDept, "dept", "dept");
            getUnitAndDept(ruleMap, hospSet, dataInfo.getColl(), unitAndDept, "dept", "coll");
            getUnitAndDept(ruleMap, univSet, dataInfo.getDept(), unitAndDept, "coll", "dept");
            getUnitAndDept(ruleMap, univSet, dataInfo.getColl(), unitAndDept, "coll", "coll");
            for (String hosp : hospSet) {
                OftenUtils.putStrSetStrAuth(hosp, auth, unitAndAuth);
                OftenUtils.putStrSetStrAuth(hosp, cnAu, unitAndAuth);
            }
            for (String univ : univSet) {
                OftenUtils.putStrSetStrAuth(univ, auth, unitAndAuth);
                OftenUtils.putStrSetStrAuth(univ, cnAu, unitAndAuth);
            }
        }
        if (allArticleInfoMap.containsKey(aid)) {
            SubjectDatabaseWord.ArticleInfo articleInfo = allArticleInfoMap.get(aid);
            int year = articleInfo.getPubYear();
            double score = articleInfo.getScore();
            String zkyDaLei = getZkyDaLei(articleInfo.getZkyDaleiInt());
            for (String unit : unitSet) {
                if (unitOrAuthAndRankMap.containsKey(unit)) {
                    int isCorrAuth = getFirstOrCorrAuth(unitAndAuth, unit, articleInfo.getCorrAuthSet());
                    int isFirstAuth = getFirstOrCorrAuth(unitAndAuth, unit, articleInfo.getFirstAuthSet());
                    OftenUtils.putStrSetInt(unit, aid, univAidListMap);
                    univAllScoreMap.compute(unit, (key, value) -> value != null ? (value + score) : score);

                    // 该单位的国自然基金
                    if (nsfcInfoMap.containsKey(unit) && !nsfcOnceSet.contains(unit)) {
                        nsfcOnceSet.add(unit);
                        Map<String, List<NsfcInfo>> authNsfcInfoMap = nsfcInfoMap.get(unit);
                        authNsfcInfoMap.values().parallelStream().forEach(nsfcInfoList -> {
                            addNsfcInfo(nsfcInfoList, univTypeYearNumMap, univTypeNameDoubleMap, unit);
                        });
                    }

                    // 如果年份是3967，则是全部的数量。比如：发文量，则是所有年的发文数量之和。
                    yearNum(univTypeYearNumMap, unit, year, zkyDaLei, articleInfo.getTimeCited(), isFirstAuth, isCorrAuth, articleInfo.getIsSixJ(), articleInfo.getIsBeida(), articleInfo.getIsCscd(), articleInfo.getIsTongJiYuan(), articleInfo.getIsZHua(), articleInfo.getIsPubmed(), articleInfo.getIsCite(), articleInfo.getFundList());
                    yearNum(univTypeYearNumMap, unit, 3967, zkyDaLei, articleInfo.getTimeCited(), isFirstAuth, isCorrAuth, articleInfo.getIsSixJ(), articleInfo.getIsBeida(), articleInfo.getIsCscd(), articleInfo.getIsTongJiYuan(), articleInfo.getIsZHua(), articleInfo.getIsPubmed(), articleInfo.getIsCite(), articleInfo.getFundList());
                    yearDouble(univTypeNameDoubleMap, unit, articleInfo.getBeiDaDaLei(), articleInfo.getBeiDaXiaoLei(), articleInfo.getZkySubject(), articleInfo.getZkySmall(), articleInfo.getCnKwList(), articleInfo.getFundList(), articleInfo.getEnKwMap(), score, unitAndDept, unitAndAuth, "auth", articleInfo.getPubMedSubject());
                    for (String cooperativeUnit : unitSet) {
                        if (!cooperativeUnit.equals(unit) && unitOrAuthAndRankMap.containsKey(unit)) {
                            putUnivTypeNameDoubleMap(univTypeNameDoubleMap, unit, "CooperativeUnit", cooperativeUnit, score);
                        }
                    }
                }
            }
        }
    }

    // authAndUnit->作者与单位的集合，authAndDept->作者与部门的集合，unitAndAuth->单位与作者的集合（作者是auth、cnAu、idAndAuth），unitSet->单位的集合，enAuthCnAuth->英文作者与中文作者建立Map，用于后续的还原。authAndAuthIdMap这里的键是只有同名同姓表的自增主键，没有作者的名字部分。
    public static void getAuth(Integer aid, SubjectDatabaseWord.DataInfo dataInfo, Map<String, Map<String, PublicEntity.TopLower>> ruleMap, Map<Integer, SubjectDatabaseWord.ArticleInfo> allArticleInfoMap, Map<String, Map<String, Map<Integer, Integer>>> authTypeYearNumMap, Map<String, Map<String, Map<String, Double>>> authTypeNameDoubleMap,
                               Map<String, Map<String, List<NsfcInfo>>> nsfcInfoMap, Set<String> nsfcOnceSet,
                               Map<String, Set<Integer>> authAidListMap, Map<String, Double> authAllScoreMap,
                               Map<String, PublicEntity.TypeAndRank> unitOrAuthAndRankMap) throws IOException {
        Set<String> unitSet = new ConcurrentHashSet<>();
        Map<String, Set<String>> authAndUnit = new ConcurrentHashMap<>();
        Map<String, Set<String>> authAndDept = new ConcurrentHashMap<>();
        Map<String, Set<String>> unitAndAuth = new ConcurrentHashMap<>();
        Map<String, PublicEntity.EnAuthAndCnAuth> enAuthCnAuth = new ConcurrentHashMap<>();
        if (aid > 0) {
            String enAuth = dataInfo.getAuth();
            String cnAu = dataInfo.getCnAu();
            enAuth = PublicUtils.reName(enAuth);
            PublicEntity.EnAuthAndCnAuth enCnAuth = new PublicEntity.EnAuthAndCnAuth();
            enCnAuth.setEnAuth(enAuth);
            enCnAuth.setCnAuth(cnAu);
            enAuthCnAuth.put(enAuth, enCnAuth);
            Set<String> hospSet = addUnitSet(dataInfo.getHosp(), unitSet, ruleMap, "hosp");
            Set<String> univSet = addUnitSet(dataInfo.getUniv(), unitSet, ruleMap, "univ");
            for (String unit : unitSet) {
                OftenUtils.putStrSetStr(enAuth, unit, authAndUnit);
            }
            getAuthDept(dataInfo.getDept(), hospSet, univSet, ruleMap, enAuth, authAndDept);
            addAuth(enAuth, enAuth, cnAu, hospSet, univSet, unitAndAuth);
        }

        if (allArticleInfoMap.containsKey(aid)) {
            SubjectDatabaseWord.ArticleInfo articleInfo = allArticleInfoMap.get(aid);
            int year = articleInfo.getPubYear();
            double score = articleInfo.getScore();
            String zkyDaLei = getZkyDaLei(articleInfo.getZkyDaleiInt());
            for (Map.Entry<String, PublicEntity.EnAuthAndCnAuth> enAuCnAu : enAuthCnAuth.entrySet()) {
                String enAuth = enAuCnAu.getKey();
                if (unitOrAuthAndRankMap.containsKey(enAuth)) { // 只要排行榜中的作者，减少计算量。
                    String auth = enAuCnAu.getValue().getEnAuth();
                    String cnAu = enAuCnAu.getValue().getCnAuth();
                    int isCorrAuth = isFirstOrCorrAuth(articleInfo.getCorrAuthSet(), auth, cnAu);
                    int isFirstAuth = isFirstOrCorrAuth(articleInfo.getFirstAuthSet(), auth, cnAu);
                    // 作者的合作单位，应该是该作者所在的整篇论文中的所有单位，去掉本人所在的单位后的集合。authUnitSet->作者个人的单位集合，循环整个单位集合，个人的不包含，即为合作单位。
                    if (authAndUnit.containsKey(auth)) {
                        Set<String> authUnitSet = authAndUnit.get(auth);
                        for (String unit : unitSet) {
                            if (!authUnitSet.contains(unit)) {
                                putUnivTypeNameDoubleMap(authTypeNameDoubleMap, enAuth, "CooperativeUnit", unit, score);
                            }
                        }
                    }
                    // 合作作者，enAuthCnAuth的键为idAndAuth，如果不是本人，即为合作作者。
                    for (Map.Entry<String, PublicEntity.EnAuthAndCnAuth> author : enAuthCnAuth.entrySet()) {
                        if (!enAuth.equals(author.getKey()) && unitOrAuthAndRankMap.containsKey(enAuth)) {
                            putUnivTypeNameDoubleMap(authTypeNameDoubleMap, enAuth, "CooperativeAuth", author.getKey(), score);
                        }
                    }

                    OftenUtils.putStrSetInt(enAuth, aid, authAidListMap);
                    authAllScoreMap.compute(enAuth, (key, value) -> value != null ? (value + score) : score);

                    // 个人的国自然基金，先判断单位，再判断单位下的作者个人。先英文作者，再中文的作者，但就是中文的作者，也算到了英文的头上，可以实现全部使用英文的作者来统一。
                    for (String unit : unitSet) {
                        if (nsfcInfoMap.containsKey(unit) && unitAndAuth.containsKey(unit) && unitAndAuth.get(unit).contains(enAuth) && !nsfcOnceSet.contains(enAuth)) {
                            Map<String, List<NsfcInfo>> authNsfcInfoMap = nsfcInfoMap.get(unit);
                            List<NsfcInfo> nsfcInfoList = new ArrayList<>();
                            if (authNsfcInfoMap.containsKey(auth)) {
                                nsfcOnceSet.add(enAuth);
                                nsfcInfoList = authNsfcInfoMap.get(auth);
                            } else if (authNsfcInfoMap.containsKey(cnAu)) {
                                nsfcOnceSet.add(enAuth);
                                nsfcInfoList = authNsfcInfoMap.get(cnAu);
                            }
                            addNsfcInfo(nsfcInfoList, authTypeYearNumMap, authTypeNameDoubleMap, enAuth);
                        }
                    }
                    yearNum(authTypeYearNumMap, enAuth, year, zkyDaLei, articleInfo.getTimeCited(), isFirstAuth, isCorrAuth, articleInfo.getIsSixJ(), articleInfo.getIsBeida(), articleInfo.getIsCscd(), articleInfo.getIsTongJiYuan(), articleInfo.getIsZHua(), articleInfo.getIsPubmed(), articleInfo.getIsCite(), articleInfo.getFundList());
                    yearNum(authTypeYearNumMap, enAuth, 3967, zkyDaLei, articleInfo.getTimeCited(), isFirstAuth, isCorrAuth, articleInfo.getIsSixJ(), articleInfo.getIsBeida(), articleInfo.getIsCscd(), articleInfo.getIsTongJiYuan(), articleInfo.getIsZHua(), articleInfo.getIsPubmed(), articleInfo.getIsCite(), articleInfo.getFundList());
                    yearDouble(authTypeNameDoubleMap, enAuth, articleInfo.getBeiDaDaLei(), articleInfo.getBeiDaXiaoLei(), articleInfo.getZkySubject(), articleInfo.getZkySmall(), articleInfo.getCnKwList(), articleInfo.getFundList(), articleInfo.getEnKwMap(), score, authAndDept, authAndUnit, "unit", articleInfo.getPubMedSubject());
                }
            }
        }
    }

    // 国自然得分，同一单位或者同一个作者，仅能计算一次。对于单位，会对所有的国自然得分（包含项目数量和项目金额）进行累加。但对于个人，则是先判断单位，再判断个人，然后再进行累加。
    public static void addNsfcInfo(List<NsfcInfo> nsfcInfoList, Map<String, Map<String, Map<Integer, Integer>>> authTypeYearNumMap, Map<String, Map<String, Map<String, Double>>> authTypeNameDoubleMap, String idAndAuth) {
        for (NsfcInfo nsfcInfo : nsfcInfoList) {
            putUnivTypeYearNumMap(authTypeYearNumMap, idAndAuth, "nsfc", nsfcInfo.getYear(), 1);
            putUnivTypeYearNumMap(authTypeYearNumMap, idAndAuth, "nsfc", 3967, 1);
            putUnivTypeNameDoubleMap(authTypeNameDoubleMap, idAndAuth, "nsfc", nsfcInfo.getProjectName(), nsfcInfo.getAmountOfMoney());
        }
    }

    // 如果输入的是作者，name即为作者idAndAuth，type为固定的单位"unit"，即该作者所对应的各个单位的得分。
    public static void yearDouble(Map<String, Map<String, Map<String, Double>>> authTypeNameDoubleMap, String name, String beiDaDaLei, String beiDaXiaoLei, List<String> zkySubject, List<String> zkySmall, List<String> cnKwList,
                                  List<String> fundList, Map<String, String> enKwMap, Double score, Map<String, Set<String>> authAndDept, Map<String, Set<String>> authAndUnit, String type, List<String> pubMedSubjectList) {
        putUnivTypeNameDoubleMap(authTypeNameDoubleMap, name, "beiDaLarge", beiDaDaLei, score);
        putUnivTypeNameDoubleMap(authTypeNameDoubleMap, name, "beiDaSmall", beiDaXiaoLei, score);
        putList(zkySubject, authTypeNameDoubleMap, name, "zkyLarge", score);
        putList(zkySmall, authTypeNameDoubleMap, name, "zkySmall", score);
        putList(cnKwList, authTypeNameDoubleMap, name, "cnKw", score);
        putMap(authTypeNameDoubleMap, authAndDept, name, "dept", score);
        putMap(authTypeNameDoubleMap, authAndUnit, name, type, score);
        putList(fundList, authTypeNameDoubleMap, name, "fund", score);
        getEnKwMap(authTypeNameDoubleMap, enKwMap, name, "enKw", score);
        putList(pubMedSubjectList, authTypeNameDoubleMap, name, "pubMedSubject", score);
    }

    // 如果输入的是单位与作者的集合（unitAndAuth），此时name为单位的名字，set为该单位下的作者的集合，type为固定的作者（"auth"），auth为作者的名字，在此作者全部使用idAndAuth或该作者所对应的唯一作者ID号，即单位下的作者均为同名同姓后的作者名字。此为重点！
    public static void putMap(Map<String, Map<String, Map<String, Double>>> univTypeNameDoubleMap, Map<String, Set<String>> unitAndAuth, String name, String type, Double score) {
        if (unitAndAuth.containsKey(name)) {
            Set<String> set = unitAndAuth.get(name);
            for (String auth : set) {
                if (type.equals("auth")) {
                    putUnivTypeNameDoubleMap(univTypeNameDoubleMap, name, type, auth, score);
//                    if (auth.contains("Łł") || NumberUtil.isInteger(auth)) {
//                        putUnivTypeNameDoubleMap(univTypeNameDoubleMap, name, type, auth, score);
//                    }
                }
                if (type.equals("unit")) {
                    putUnivTypeNameDoubleMap(univTypeNameDoubleMap, name, type, auth, score);
                }
            }
        }
    }

    // 如果输入的是单位，name为单位unit的名字，如果输入的作者，name为单位idAndAuth的名字。zkyDaLei为中科院的分区值。
    public static void yearNum(Map<String, Map<String, Map<Integer, Integer>>> univTypeYearNumMap, String name, Integer year, String zkyDaLei, Integer cited, Integer firstAuth, Integer corrAuth, Integer isSixJ, Integer isBeida, Integer isCscd, Integer isTongJiYuan,
                               Integer isZHua, Integer isPubmed, Integer isCite, List<String> fundList) {
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "paper", year, 1);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "cited", year, cited);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "firstAuth", year, firstAuth);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "corrAuth", year, corrAuth);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "isSixJ", year, isSixJ);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, zkyDaLei, year, 1);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "isBeida", year, isBeida);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "isCscd", year, isCscd);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "isTongJiYuan", year, isTongJiYuan);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "isZHua", year, isZHua);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "isPubmed", year, isPubmed);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "isCite", year, isCite);
        putUnivTypeYearNumMap(univTypeYearNumMap, name, "fund", year, fundList.size());
    }

    // 判断该单位下的所有作者，是否属于第一作者或者通讯作者。单位与作者的集合unitAndAuth，其中作者的集合包含了三种形式（auth、cnAu、idAndAuth），而firstAuthSet一般只有两种形式（auth、cnAu），所以是可以的。
    public static Integer getFirstOrCorrAuth(Map<String, Set<String>> unitAndAuth, String unit, Set<String> firstAuthSet) {
        if (unitAndAuth.containsKey(unit)) {
            Set<String> allAuth = unitAndAuth.get(unit);
            for (String author : allAuth) {
                String s = DelOtherLower(author);
                if (firstAuthSet.contains(s)) {
                    return 1;
                }
            }
        }
        return 0;
    }

    //删除 字符 汉字
    public static String DelOtherLower(String str) {
        return str.replaceAll("[^a-zA-Z0-9\\u4E00-\\u9FA5]", "").toLowerCase();
    }

    // 英文关键词的enKwMap，键是自由词，值为斯坦福后的，这里取斯坦福后的。如果输入的是作者，name为作者idAndAuth的名字。
    public static void getEnKwMap(Map<String, Map<String, Map<String, Double>>> authTypeNameDoubleMap, Map<String, String> enKwMap, String name, String type, Double score) {
        for (Map.Entry<String, String> map : enKwMap.entrySet()) {
            putUnivTypeNameDoubleMap(authTypeNameDoubleMap, name, type, map.getValue(), score);
        }
    }

    // 为中科院分区值前后加上文字，因中科院只有1，2，3，4四个分区，故大于0而小于5，其它为空。
    public static String getZkyDaLei(Integer zkyDaLei) {
        if (zkyDaLei > 0 && zkyDaLei < 5) {
            return "中科院" + zkyDaLei.toString() + "区";
        }
        return "";
    }

//    @Data
//    public static class EnAuthAndCnAuth {
//        private String auth = "";
//        private String cnAu = "";
//    }

    // 如果第一作者Set包括该作者，返回1，否则返回0。
    public static Integer isFirstOrCorrAuth(Set<String> corrAuthSet, String auth, String cnAu) {
        if (corrAuthSet.contains(auth) || corrAuthSet.contains(cnAu)) {
            return 1;
        }
        return 0;
    }

    // 获取作者的部门，如果作者所在的是医院，则以“科”为结尾，如果在大学，则以“系”为结尾。医院优先（如果同时属于医院和大学，则以医院为主），没有医院时，才考虑使用大学。
    public static void getAuthDept(Set<String> deptSet, Set<String> hospSet, Set<String> univSet, Map<String, Map<String, PublicEntity.TopLower>> ruleMap, String idAndAuth, Map<String, Set<String>> authAndDept) {
        if (hospSet.size() > 0) {
            for (String dept : deptSet) {
                if (ruleMap.containsKey("dept") && ruleMap.get("dept").containsKey(dept)) {
                    String str = ruleMap.get("dept").get(dept).getTop();
                    if (str.endsWith("系")) {
                        str = str.substring(0, str.length() - "系".length()) + "科";
                    }
                    OftenUtils.putStrSetStr(idAndAuth, str, authAndDept);
                } else {
                    OftenUtils.putStrSetStr(idAndAuth, dept, authAndDept);
                }
            }
        } else if (univSet.size() > 0) {
            for (String dept : deptSet) {
                if (ruleMap.containsKey("coll") && ruleMap.get("coll").containsKey(dept)) {
                    String str = ruleMap.get("coll").get(dept).getTop();
                    if (str.endsWith("科")) {
                        str = str.substring(0, str.length() - "科".length()) + "系";
                    }
                    OftenUtils.putStrSetStr(idAndAuth, str, authAndDept);
                } else {
                    OftenUtils.putStrSetStr(idAndAuth, dept, authAndDept);
                }
            }
        }
    }

    // 对Map进行排序，并对得分进行四舍五入，保留后4位小数。Map结构为类型（比如：zkySmall）、名字（比如：传染病学）、累加得分。
    public static Map<String, Map<String, Double>> changeDouMap(Map<String, Map<String, Double>> typeNameDoubleMap) {
        Map<String, Map<String, Double>> saveTypeNameDoubleMap = new ConcurrentHashMap<>();
        for (String type : typeNameDoubleMap.keySet()) {
            Map<String, Double> nameDouble = typeNameDoubleMap.get(type);
            Map<String, Double> sortMap = OftenUtils.mapSortValueDouLen(nameDouble, 100);
            Map<String, Double> temp = new LinkedHashMap<>();
            for (Map.Entry<String, Double> strDou : sortMap.entrySet()) {
                temp.put(OftenUtils.emojiConvert(strDou.getKey()), OftenUtils.takeFourDigits(strDou.getValue()));
            }
            saveTypeNameDoubleMap.put(type, temp);
        }
        return saveTypeNameDoubleMap;
    }

    // 执行插表，对处理后的单位信息，进行插表。univAidListMap为各单位所对应的文章ID号集合。这里同样对得分部分进行排序和取前100个。
    public static void insertUnitStatisticsTable(String saveTable, Map<String, Map<String, Map<Integer, Integer>>> univTypeYearNumMap, Map<String, Map<String, Map<String, Double>>> univTypeNameDoubleMap, Map<String, Set<Integer>> univAidListMap,
                                                 Map<String, Double> univAllScoreMap, Map<String, PublicEntity.TypeAndRank> unitOrAuthAndRankMap, List<PublicEntity.TableField> tableList, boolean isInsertTable) {
        univTypeYearNumMap.keySet().parallelStream().forEach(unit -> {
            Map<String, Map<Integer, Integer>> typeYearNumMap = univTypeYearNumMap.get(unit);
            int paperNum = 0;
            if (typeYearNumMap.containsKey("paper") && typeYearNumMap.get("paper").containsKey(3967)) {
                paperNum = typeYearNumMap.get("paper").get(3967);
            }
            Map<String, Map<String, Double>> typeNameDoubleMap = new ConcurrentHashMap<>();
            if (univTypeNameDoubleMap.containsKey(unit)) {
                typeNameDoubleMap = changeDouMap(univTypeNameDoubleMap.get(unit));
            }
            Set<Integer> aidList = new ConcurrentHashSet<>();
            if (univAidListMap.containsKey(unit)) {
                aidList = univAidListMap.get(unit);
            }
            double score = 0.0;
            if (univAllScoreMap.containsKey(unit)) {
                score = OftenUtils.takeFourDigits(univAllScoreMap.get(unit));
            }
            if (unitOrAuthAndRankMap.containsKey(unit)) {
                PublicEntity.TypeAndRank typeAndRank = unitOrAuthAndRankMap.get(unit);
                String key = typeYearNumMap.size() > 0 ? JSONUtil.toJsonStr(typeYearNumMap) : "";
                String value = typeNameDoubleMap.size() > 0 ? JSONUtil.toJsonStr(typeNameDoubleMap) : "";
                String aid_list = aidList.size() > 0 ? JSONUtil.toJsonStr(aidList) : "";
                unit = OftenUtils.emojiConvert(unit);
                PublicUtils.insertTable(saveTable, key, value, typeAndRank.getCode(), "单位信息_" + typeAndRank.getType(), typeAndRank.getRank(), score, paperNum, typeAndRank.getSpecialist(), typeAndRank.getJid(), unit, aid_list, tableList, isInsertTable);
            }
        });
    }

    // Map结构为：单位或作者个人的名字，类型（比如：zkySmall或enKw）、类型的名字（比如：传染病学）、累加得分。
    public static void putUnivTypeNameDoubleMap(Map<String, Map<String, Map<String, Double>>> univTypeNameDoubleMap, String unitName, String statisticType, String name, double score) {
        if (name.length() > 1 && statisticType.length() > 1) {
            univTypeNameDoubleMap.compute(unitName, (k, v) -> {
                if (v == null) {
                    v = new ConcurrentHashMap<>();
                }
                v.compute(statisticType, (k1, v1) -> {
                    if (v1 == null) {
                        v1 = new ConcurrentHashMap<>();
                    }
                    v1.compute(name, (key, value) -> value != null ? (value + score) : score);
                    return v1;
                });
                return v;
            });
        }
    }

    // Map结构为：单位或作者个人的名字，类型（比如：isSixJ或firstAuth）、年份（比如：2018）、累加发文的篇数。
    public static void putUnivTypeYearNumMap(Map<String, Map<String, Map<Integer, Integer>>> univTypeYearNumMap, String unitName, String statisticType, Integer year, Integer num) {
        if (num > 0 && statisticType.length() > 1) {
            univTypeYearNumMap.compute(unitName, (k, v) -> {
                if (v == null) {
                    v = new ConcurrentHashMap<>();
                }
                v.compute(statisticType, (k1, v1) -> {
                    if (v1 == null) {
                        v1 = new ConcurrentHashMap<>();
                    }
                    v1.compute(year, (key, value) -> value != null ? (value + num) : num);
                    return v1;
                });
                return v;
            });
        }
    }

    // 获取各大学或医院所对应的院系或科室信息。参数中code指的是deptSet时值为“dept”，collSet时值为“coll”。collOrDept指的是unitSet为hospSet时值为“dept”，univSet时值为“coll”。
    public static void getUnitAndDept(Map<String, Map<String, PublicEntity.TopLower>> ruleMap, Set<String> unitSet, Set<String> deptSet, Map<String, Set<String>> unitAndDeptMap, String collOrDept, String code) {
        for (String unit : unitSet) {
            for (String dept : deptSet) {
                if (ruleMap.containsKey(code) && ruleMap.get(code).containsKey(dept)) {
                    String str = ruleMap.get(code).get(dept).getTop();
                    if (collOrDept.equals("coll")) {
                        if (str.endsWith("科")) {
                            str = str.substring(0, str.length() - "科".length()) + "系";
                        }
                    }
                    if (collOrDept.equals("dept")) {
                        if (str.endsWith("系")) {
                            str = str.substring(0, str.length() - "系".length()) + "科";
                        }
                    }
                    OftenUtils.putStrSetStr(unit, str, unitAndDeptMap);
                } else {
                    OftenUtils.putStrSetStr(unit, dept, unitAndDeptMap);
                }
            }
        }
    }

    // 如果规则中存在单位的标准写法，则取标准的写法，如果不存在，则取原始值。
    public static Set<String> addUnitSet(Set<String> unitSet, Set<String> allHospAndUnivSet, Map<String, Map<String, PublicEntity.TopLower>> ruleMap, String code) {
        Set<String> set = new HashSet<>();
        for (String unit : unitSet) {
            if (ruleMap.containsKey(code) && ruleMap.get(code).containsKey(unit)) {
                String standard = ruleMap.get(code).get(unit).getTop();
                set.add(standard);
                allHospAndUnivSet.add(standard);
            } else {
                set.add(unit);
                allHospAndUnivSet.add(unit);
            }
        }
        return set;
    }

    // 将每位作者归类到各自的单位下，此处作者分为三种形式。
    public static void addAuth(String idAndAuth, String auth, String cnAu, Set<String> hospSet, Set<String> univSet, Map<String, Set<String>> unitAndAuth) {
        for (String hosp : hospSet) {
            OftenUtils.putStrSetStr(hosp, idAndAuth, unitAndAuth);
            OftenUtils.putStrSetStr(hosp, auth, unitAndAuth);
            OftenUtils.putStrSetStr(hosp, cnAu, unitAndAuth);
        }
        for (String univ : univSet) {
            OftenUtils.putStrSetStr(univ, idAndAuth, unitAndAuth);
            OftenUtils.putStrSetStr(univ, auth, unitAndAuth);
            OftenUtils.putStrSetStr(univ, cnAu, unitAndAuth);
        }
    }

    // 如果输入的单位，name为单位unit的名字，如果输入的List为cnKwList，则type为”cnKw“，即遍历List，对每一个值均给予加入univTypeNameDoubleMap，进行累加得分的计算。
    public static void putList(List<String> cnKwList, Map<String, Map<String, Map<String, Double>>> univTypeNameDoubleMap, String name, String type, Double score) {
        for (String cnKw : cnKwList) {
            putUnivTypeNameDoubleMap(univTypeNameDoubleMap, name, type, cnKw, score);
        }
    }

    // 获取基金的信息，如果单位的名称有标准的，采用标准的，如果没有标准的，按表中的名称。  var2`,`year`,`var7`,`var5`,`var6
    public static void getNsfc(Entity entry, Map<String, Map<String, List<NsfcInfo>>> nsfcInfoMap, Map<String, Map<String, PublicEntity.TopLower>> ruleMap) throws SQLException {
        String projectName = OftenUtils.getStrField(entry, "var2");
        if (projectName.length() > 2) {
            NsfcInfo nsfcInfo = new NsfcInfo();
            nsfcInfo.setProjectName(projectName);
            nsfcInfo.setYear(OftenUtils.getIntField(entry, "year"));
            nsfcInfo.setAmountOfMoney(OftenUtils.strToDouble(OftenUtils.getStrField(entry, "var7").replace("万元", "").replace("-", "").replaceAll(" +", "")));

            String var5 = OftenUtils.getStrField(entry, "var5");
            String unit = var5;
            if (ruleMap.containsKey("univ") && ruleMap.get("univ").containsKey(var5)) {
                unit = ruleMap.get("univ").get(var5).getTop();
            }
            if (ruleMap.containsKey("hosp") && ruleMap.get("hosp").containsKey(var5)) {
                unit = ruleMap.get("hosp").get(var5).getTop();
            }
            nsfcInfo.setUnit(unit);
            String auth = PublicUtils.authorBlankSpace(PublicUtils.reName(OftenUtils.getStrField(entry, "var6")));
            nsfcInfo.setAuth(auth);
            if (unit.length() > 2 && auth.length() > 1) {
                nsfcInfoMap.compute(unit, (k, v) -> {
                    if (v == null) {
                        v = new ConcurrentHashMap<>();
                    }
                    v.compute(auth, (k1, v1) -> {
                        if (v1 == null) {
                            v1 = Collections.synchronizedList(new ArrayList<>());
                        }
                        v1.add(nsfcInfo);
                        return v1;
                    });
                    return v;
                });
            }
        }
    }

    @ProtobufClass
    @Data
    public static class NsfcInfo {
        private Integer year = 0;//国自然基金申报年
        private String unit = "";//申报单位名称
        private String auth = "";//申报人
        private String projectName = "";//申报的项目名称
        private double amountOfMoney = 0;//金额
    }

//end
}
